package gminet.infra.db;

/**
 * Insira a descri��o do tipo aqui. Data de cria��o: (19/1/2002 14:40:56)
 * @autor: Administrator
 */

import gminet.infra.toolkit.StringToolkit;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

class ConnectionPool {

    private static final long TIME_OUT = 30 * 1000;

    private int checkedOut = 0;

    private List<Connection> freeConnections;

    private List<DBConnection> busyConnections;

    private String URL;

    private String user;

    private String password;

    private int maxConns;

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 14:44:52)
     * 
     * @param URL
     *            java.lang.String
     * @param user
     *            java.lang.String
     * @param password
     *            java.lang.String
     * @param maxConns
     *            int
     * @param initConns
     *            int
     */

    protected ConnectionPool(String URL, String user, String password, int maxConns, int initConns) throws SQLException {

	this.URL = URL;
	this.user = user;
	this.password = password;
	this.maxConns = maxConns;
	this.freeConnections = new ArrayList<Connection>(initConns);
	this.busyConnections = new ArrayList<DBConnection>(initConns);
	initConnections(initConns);
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 14:53:52)
     * 
     * @param initConns
     *            int
     */
    private void initConnections(int initConns) throws SQLException {

	for (int i = 0; i < initConns; i++) {
	    addConnectionInPool();
	}
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 14:53:52)
     * 
     * @param initConns
     *            int
     */
    private Connection addConnectionInPool() throws SQLException {

	Connection realConnection = createRealConnection();
	freeConnections.add(realConnection);
	return realConnection;
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 15:35:39)
     * 
     * @return java.sql.Connection
     * @exception java.sql.SQLException
     *                A descri��o da exce��o.
     */
    private Connection createRealConnection() throws SQLException {

	Connection realConnection = null;
	if (StringToolkit.isFill(user)) {
	    realConnection = DriverManager.getConnection(URL, user, password);
	} else {
	    realConnection = DriverManager.getConnection(URL);
	}
	return realConnection;
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 14:59:01)
     * 
     * @return java.sql.Connection
     * @param timeOut
     *            long
     * @exception java.sql.SQLException
     *                A descri��o da exce��o.
     */
    protected synchronized DBConnection getDBConnection() throws SQLException {

	long startTime = System.currentTimeMillis();
	DBConnection dbConn = null;
	while ((dbConn = getPooledConnection()) == null) {
	    try {
		wait(1000);
	    } catch (InterruptedException e) {
	    }
	    long remaining = TIME_OUT - (System.currentTimeMillis() - startTime);
	    if (remaining <= 0) {
		throw new SQLException("getConnection() time-out");
	    }
	}
	if (!isConnectionOk(dbConn)) {
	    return getDBConnection();
	}
	checkedOut++;
	return dbConn;
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 15:32:48)
     * 
     * @return java.sql.Connection
     * @exception java.sql.SQLException
     *                A descri��o da exce��o.
     */
    private DBConnection getPooledConnection() throws SQLException {

	DBConnection dbConn = null;
	Connection realConnection = null;
	if (freeConnections.size() > 0) {
	    realConnection = (Connection) freeConnections.get(0);
	    freeConnections.remove(0);
	    if (realConnection == null || realConnection.isClosed()) {
		realConnection = addConnectionInPool();
		freeConnections.remove(realConnection);
	    }
	    try {
		dbConn = new DBConnection(realConnection, TIME_OUT - 1000);
	    } catch (SQLException sqle) {
		realConnection = addConnectionInPool();
		freeConnections.remove(realConnection);
		dbConn = new DBConnection(realConnection, TIME_OUT - 1000);
	    }
	} else if (verifyTerminatedConnections()) {
	    dbConn = getPooledConnection();
	} else if (maxConns == 0 || checkedOut < maxConns) {
	    realConnection = addConnectionInPool();
	}
	if (dbConn != null) {
	    busyConnections.add(dbConn);
	}
	return dbConn;
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 15:28:17)
     * 
     * @return boolean
     * @param conn
     *            java.sql.Connection
     */
    private boolean verifyTerminatedConnections() {

	boolean isTerminated = false;
	for (DBConnection dbConnection : busyConnections) {
	    try {
		if (dbConnection.isTerminated()) {
		    releaseConnection(dbConnection);
		    isTerminated = true;
		}
	    } catch (SQLException sqle) {
		releaseConnection(dbConnection, true);
		isTerminated = true;
	    }
	}
	return isTerminated;
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 15:28:17)
     * 
     * @return boolean
     * @param conn
     *            java.sql.Connection
     */
    private boolean isConnectionOk(DBConnection dbConnection) {

	Statement testStmt = null;
	try {
	    if (!dbConnection.isClosed()) {
		testStmt = dbConnection.createStatement();
		testStmt.close();
	    } else {
		return false;
	    }
	} catch (SQLException e) {
	    if (testStmt != null) {
		try {
		    testStmt.close();
		} catch (SQLException se) {
		}
	    }
	    return false;
	}
	return true;
    }

    protected synchronized void releaseConnection(DBConnection dbConnection) {

	releaseConnection(dbConnection, false);
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 15:38:43)
     * 
     * @param conn
     *            java.sql.Connection
     */
    private synchronized void releaseConnection(DBConnection dbConnection, boolean forceNew) {

	busyConnections.remove(dbConnection);
	Connection conn = null;
	try {
	    conn = dbConnection.getRealConnection();
	} catch (SQLException sqle) {
	    conn = null;
	}
	if (conn == null || forceNew) {
	    try {
		addConnectionInPool();
	    } catch (SQLException sqle2) {
		//
	    }
	} else {
	    freeConnections.add(conn);
	}
	checkedOut--;
	notifyAll();
    }

    /**
     * Insira a descri��o do m�todo aqui. Data de cria��o: (19/1/2002 15:40:20)
     */
    protected synchronized void close() {

	for (DBConnection dbConnection : busyConnections) {
	    try {
		dbConnection.close();
	    } catch (SQLException e) {
	    }
	}
	for (Connection connection : freeConnections) {
	    try {
		connection.close();
	    } catch (SQLException e) {
	    }
	}
	freeConnections.clear();
    }
}
